//
// File:	Animating.cpp
// Author:	Thomas TISSOT-DUPONT
// Login:	<thomas.tissotdupont@free.fr>
//
// Created:	15/04/2010 at 11:37:46
// Purpose:	
//
#include "Base.hpp"
#include "ResourceManager.hpp"
#include "ResourceManagerTypes.hpp"
#include "Animating.hpp"
#include "Parser.hpp"
#include "Trace.hpp"

//------------------------------------------------------------------
// Constructor / Destructor
//------------------------------------------------------------------
Animating::Animating()
	:	m_animations(),
		m_ivFrameSize(0, 0),
		m_pCurrentAnimation(NULL)
{
	Trace::debug << "SIZEOF ANIMATING: " << sizeof(*this) << std::endl;
}

Animating::~Animating()
{
	std::map<std::string, IAnimation *>::iterator it = m_animations.begin();
	std::map<std::string, IAnimation *>::iterator ite = m_animations.end();
	for (; it != ite; ++it)
	{
		delete it->second;
	}
}

//------------------------------------------------------------------
// Update
//------------------------------------------------------------------
void Animating::Update()
{
	if (m_pCurrentAnimation != NULL && m_pCurrentAnimation->IsPlaying())
		m_pCurrentAnimation->Update();
}

//------------------------------------------------------------------
// Render
//------------------------------------------------------------------
void Animating::Render(float flX, float flY)
{
	if (m_pCurrentAnimation != NULL)
	{
		//FRIENDSHIP IS TEMPORARY FOR TESTING PURPOSE
		Animation *pAnimation = dynamic_cast<Animation *>(m_pCurrentAnimation);
		if (pAnimation->m_framesVector.size() > 0)
		{
			Animation::Frame frame = pAnimation->m_framesVector[static_cast<unsigned int>(pAnimation->m_flCurrentFrame)];
			sf::Sprite::SetSubRect(sf::IntRect(frame.left, frame.top, frame.right, frame.bottom));
			BaseRenderable::Render(flX, flY);
		}
	}
}

//------------------------------------------------------------------
// Render Color
//------------------------------------------------------------------
void Animating::SetRenderColor(unsigned char r, unsigned char g, unsigned char b, unsigned char a /* = 255 */)
{
	BaseRenderable::SetRenderColor(r, g, b, a);
}

//------------------------------------------------------------------
// Play / Pause / Resume / Stop Animation
//------------------------------------------------------------------
bool Animating::PlayAnimation(const std::string &animName)
{
	//Stop any playing animation
	StopAnimation();
	if ((m_pCurrentAnimation = GetAnimationByName(animName)) == NULL)
	{
		Trace::warning << "Animating::PlayAnimation: animation not found: " << animName << std::endl;
		return false;
	}
	m_pCurrentAnimation->Play();
	return true;
}

void Animating::PauseAnimation()
{
	if (m_pCurrentAnimation != NULL)
		m_pCurrentAnimation->Pause();
}

void Animating::ResumeAnimation()
{
	if (m_pCurrentAnimation != NULL)
		m_pCurrentAnimation->Play();
}

void Animating::StopAnimation()
{
	if (m_pCurrentAnimation != NULL)
		m_pCurrentAnimation->Stop();
	m_pCurrentAnimation = NULL;
}

//------------------------------------------------------------------
// Add Animation
//------------------------------------------------------------------
bool Animating::AddAnimation(IAnimation &anim)
{
	//Cannot override an existing animation
	if (GetAnimationByName(anim.GetName()) != NULL)
		return false;

	//Cannot add an unnamed animation
	if (anim.GetName().empty())
		return false;

	//Add the animation to the list
	m_animations[anim.GetName()] = &anim;
	return true;
}

//------------------------------------------------------------------
// Current Animation
//------------------------------------------------------------------
IAnimation *Animating::GetCurrentAnimation() const
{
	return m_pCurrentAnimation;
}

//------------------------------------------------------------------
// Animation by Name
//------------------------------------------------------------------
IAnimation *Animating::GetAnimationByName(const std::string &animName) const
{
	std::map<std::string, IAnimation *>::const_iterator it = m_animations.begin();
	std::map<std::string, IAnimation *>::const_iterator ite = m_animations.end();
	for (; it != ite; ++it)
	{
		//We double-check-up the animation name
		if (it->second != NULL && it->second->GetName() == animName)
			return it->second;
	}
	return NULL;
}

//------------------------------------------------------------------
// Clear
//------------------------------------------------------------------
void Animating::Clear()
{
	std::map<std::string, IAnimation *>::iterator it = m_animations.begin();
	std::map<std::string, IAnimation *>::iterator ite = m_animations.end();
	for (; it != ite; ++it)
	{
		delete it->second;
	}
	m_animations.clear();
	m_ivFrameSize = sf::Vector2i(0, 0);
	StopAnimation();
}

//------------------------------------------------------------------
// Load From File
//------------------------------------------------------------------
bool Animating::LoadFromFile(const std::string &fileName)
{
	//Mendatory Keys
	std::list<std::string> spriteMendatoryKeys;
	spriteMendatoryKeys.push_back("image");
	spriteMendatoryKeys.push_back("frame_w");
	spriteMendatoryKeys.push_back("frame_h");
	std::list<std::string> animationMendatoryKeys;
	animationMendatoryKeys.push_back("name");

	//Parse file
	Parser parser;
	if (!parser.ParseFromFile(fileName))
		return false;

	//Get root element
	const Parser::Element *pSprite = parser.GetRootElement().FindChild("sprite");
	if (pSprite == NULL)
		return false;
	
	//Check sprite mendatory keys
	if (pSprite->HasKeys(spriteMendatoryKeys) == false)
		return false;

	//Clear the animations list
	Clear();

	//Retrieve the sprite key-values
	sf::Image *pImage = ImageResourceManager::GetInstance().Find(pSprite->GetValueForKey("image").ToString());
	if (pImage == NULL)
		return false;
	sf::Sprite::SetImage(*pImage);
	m_ivFrameSize = sf::Vector2i(pSprite->GetValueForKey("frame_w").ToUInt(), pSprite->GetValueForKey("frame_h").ToUInt());

	//Loop through the sprite animations list
	std::list<const Parser::Element *> spriteAnimationsList = pSprite->FindChildren("animation");
	std::list<const Parser::Element *>::const_iterator it = spriteAnimationsList.begin();
	std::list<const Parser::Element *>::const_iterator ite = spriteAnimationsList.end();
	for (; it != ite; ++it)
	{
		//Check animation mendatory keys
		if ((*it)->HasKeys(animationMendatoryKeys) == false)
			continue;

		//Retrieve the animation key-values
		std::string animName = (*it)->GetValueForKey("name").ToString();
		bool bLoop = (*it)->GetValueForKey("loop", false).ToBool();
		bool bFlipped = (*it)->GetValueForKey("flip", false).ToBool();
		float flFrameRate = (*it)->GetValueForKey("fps", 8.0f).ToFloat();
		int iRangeStart = (*it)->GetValueForKey("start", -1).ToInt();
		int iRangeEnd = (*it)->GetValueForKey("end", -1).ToInt();

		//Create the animation
		IAnimation *pAnimation = new Animation(sf::Sprite::GetImage(), animName, m_ivFrameSize, flFrameRate, bFlipped, bLoop, iRangeStart, iRangeEnd);
		if (pAnimation != NULL)
		{
			AddAnimation(*pAnimation);
		}
	}
	return true;
}

//------------------------------------------------------------------
// Factory
//------------------------------------------------------------------
IAnimating *Animating::CreateAnimatingFromFile(const std::string &fileName)
{
	IAnimating *pAnimating = new Animating();
	if (pAnimating->LoadFromFile(fileName) == false)
	{
		delete pAnimating;
		return NULL;
	}
	return pAnimating;
}

//------------------------------------------------------------------
// Frame Size
//------------------------------------------------------------------
int Animating::GetFrameWidth() const
{
	return m_ivFrameSize.x;
}

int Animating::GetFrameHeight() const
{
	return m_ivFrameSize.y;
}
